#include <windows.h>

#include "lib.h"

#include "rdconn.h"
#include "rdgame.h"
#include "rdsql.h"
#include "log.h"

#include "stdncerr.h"
#include "badpacketmsgs.h"

RedDogConnection::RedDogConnection(Server* server, SOCKET client)
	: Connection(server, client)
{
	game = (RedDogGame*)0;

	minBet = 500;	// purposefully set maxBet < minBet to disallow playing
	maxBet = 100;	// games where these values don't come from database
	compFactor = 0;

	loggedOn = false;

	casinoSpecific = (void*)0;

    // kharmon.  11-15-06.  #16921.  Zero out buffers before using them.
    msglen = 0;
    memset(msgbuf, 0, sizeof(msgbuf));

	#ifdef FORCEDSHOE
    m_pid="";
    #endif
}

RedDogConnection::~RedDogConnection()
{
	delete game;
	game = (RedDogGame*)0;
	loggedOn = false;

	// CSN-7075: New Authentication
	trackLogoutSession();

	removeCasinoSpecific();
}

void
RedDogConnection::gameDone()
{
	trackRemoveSavedGame();

	delete game;
	game = (RedDogGame*)0;
}

void
RedDogConnection::msgStart()
{
	msglen = 0;
}

void
RedDogConnection::msgAdd(uint8 Byte)
{
	msgbuf[msglen++] = Byte;
}

void
RedDogConnection::msgAdd(int32 SignedLong)
{
	msgbuf[msglen++] = (uint8)((SignedLong) & 0xff);
	msgbuf[msglen++] = (uint8)(((SignedLong) >> 8) & 0xff);
	msgbuf[msglen++] = (uint8)(((SignedLong) >> 16) & 0xff);
	msgbuf[msglen++] = (uint8)(((SignedLong) >> 24) & 0xff);
}

uint8*
RedDogConnection::msgGetBuf()
{
	return msgbuf + msglen;
}

void
RedDogConnection::msgReleaseBuf(int32 Amt)
{
	msglen += Amt;
}

void
RedDogConnection::msgEnd()
{
	msglen = encode(msgbuf, obuf + oused + 1, msglen);
	msglen++;	// include msg len byte
	obuf[oused] = (uint8)(msglen & 0xff);
	oused += msglen;
}

void
RedDogConnection::sendStopMsg()
{
	msgStart();
	msgAdd((uint8)'!');
	msgEnd();
}

int32
RedDogConnection::msgRemove(uint8* Src, int32* pResult)
{
	*pResult = (int32)(
		(Src[0] & 0xFF) |
		((Src[1] << 8) & 0xFF00) |
		((Src[2] << 16) & 0xFF0000) |
		((Src[3] << 24) & 0xFF000000));

	return 4;
}

bool8
RedDogConnection::getResponse()
{
	int32 ieat = 0, remain = iused, len;
	uint8* src = ibuf;
	int32 balance;

	// [AO 2007-10-01] We can send just one reply for certain repeating messages.
	// We're not handling partial buffers and incomplete messages but that's OK.
	char last_message = 0;

	while (remain > 0)
	{
		if (remain < src[0])
			break;

		// Handle an invalidly formatted message of length zero.
		if (src[0] <= 1)        // kharmon.  11-16-06.  #16921. 
		{
			ieat += 1;
			remain -= 1;
			src += 1;
			continue;
		}

        // kharmon.  11-15-06.  #16921.  Zero out buffers before using them.
	    msglen = 0;
	    memset(msgbuf, 0, sizeof(msgbuf));

		len = decode(src + 1, msgbuf, src[0] - 1);
		ieat += src[0];
		remain -= src[0];
		src += src[0];

		switch (msgbuf[0])
		{
			case 'L':
			{
				int32 err = 0;

				if (!parseCasinoLogin(5, &err))
				{
					msgStart();
					msgAdd((uint8)'E');
					msgAdd((uint8)(err & 0xff));
					msgEnd();
					break;
				}

				handleLogin(len);

				break;
			}
			
			case 'M':	//Unicode
				{
					switch (msgbuf[1])
					{
					case 'L':
						{
							int32 err = 0;
							
							if (!parseUnicodeCasinoLogin(6, &err))
							{
								msgStart();
								msgAdd((uint8)'E');
								msgAdd((uint8)(err & 0xff));
								msgEnd();
								break;
							}
							
							handleLogin(len);
						}
						break;
						
						
					default:
						DEBUGMSG(("AccountConnection::handleUnicodeMessages() unhandled message: '%c'", msgbuf[1]));
					}

					break;
				}

			case 'D':
			{
				if (!loggedOn)
				{
					Log::msgWarning("Deal: not logged on...invalid bet  ");

					trackaddBadPacket(TRY_DEAL_NOT_LOGGED_ON, msgbuf[0],(char*)msgbuf,len);

					msgStart();
					msgAdd((uint8)'B');
					msgEnd();

					break;
				}

				if (isServerStopping())
				{
					sendStopMsg();
					break;
				}

				// [AO 2008-01-16] HA: verify the user is still logged on
				if (!ValidateSession())
					break;

				int32 i, bet;

				i = 1;
				i += msgRemove(msgbuf + i, &bet);

				trackGetBalance(&balance);

				if (game)
				{
					msgStart();
					msgAdd((uint8)'G');
					msgEnd();
				}
				else if (balance > 0 && balance >= bet &&
					bet >= minBet && bet <= maxBet)
				{
					bool8 aborted = false;

					game = new RedDogGame(this, bet);

					// balance is adjusted inside of trackNewGame
					//
					if (game && trackNewGame(bet, &balance))
					{
						if (!game->deal())
							aborted = true;
					}
					else
						aborted = true;

					if (aborted)
					{
						Log::msgError("  can't create game  ");

						delete game;
						game = (RedDogGame*)0;

						msgStart();
						msgAdd((uint8)'N');
						msgEnd();
					}
				}
				else if (balance > 0)	// bad bet
				{
					Log::msgWarning("  invalid bet  ");

					msgStart();
					msgAdd((uint8)'B');
					msgEnd();
				}
				else					// not enough money in account
				{
					Log::msgWarning("  out of money  ");

					msgStart();
					msgAdd((uint8)'$');
					msgEnd();
				}
				break;
			}

			case 'R':	// Raise
			{
				// [AO 2008-01-16] HA: verify the user is still logged on
				if (!ValidateSession())
					break;

				int32 i;
				int32 raiseBet;

				i = 1;
				i += msgRemove(msgbuf + i, &raiseBet);

				if (game)
				{
					trackGetBalance(&balance);

					if(!(game->canRaise()))
					{
						Log::msgWarning(" tried to raise when already raised ");

						trackaddBadPacket(TRY_RAISE_ALREADY_RAISE, msgbuf[0], (char*)msgbuf,len);

							msgStart();
							msgAdd((uint8)'N');
							msgEnd();
					}
	//				else if (balance >= (raiseBet + game->getBet()))
					else if (raiseBet >= 0 && balance >= raiseBet)
					{
						// the balance has already been adjusted to remove
						//  the ante, so just check the balance against the
						//  raise...
						if(raiseBet > game->getBet())
						{
							Log::msgWarning(" trying to raise beyond bet limit! ");

							trackaddBadPacket(TRY_RAISE_BEYOND_LIMIT, msgbuf[0], (char*)msgbuf,len);

							raiseBet = game->getBet();
						}

						if (!game->raise(raiseBet, balance))
						{
							msgStart();
							msgAdd((uint8)'N');
							msgEnd();
						}
					}
					else
					{
						Log::msgWarning("  out of money  ");

						msgStart();
						msgAdd((uint8)'$');
						msgEnd();
					}
				}
				else
				{
					Log::msgWarning("no game!");

					trackaddBadPacket(TRY_RAISE_NO_GAME, msgbuf[0], (char*)msgbuf,len);

					msgStart();
					msgAdd((uint8)'N');
					msgEnd();
				}
				break;
			}

            case 'm':  // Balance Request
            {
				if (isServerStopping())
	            {
		            sendStopMsg();
		            break;
	            }

				// [AO 2007-10-01] Ignore repeating messages
				// [AO 2008-01-16] HA: verify the user is still logged on
				if (msgbuf[0] == last_message || !ValidateSession())
					break;

	            //rule1: gotta be logged on
                if( loggedOn && trackGetBalance(&balance) )
                {
                    msgStart();
                    msgAdd((uint8)'m');
                    msgAdd((int32)balance);
                    msgEnd();
                }

	            break;
            }

			default:

				trackaddBadPacket(GETREPONSE_DEFAULT_CASE, msgbuf[0], (char*)msgbuf, len);

				Log::msgWarning("Connection::getResponse:  Unknown input"
					" (0x%02x)  ", msgbuf[0]);
				msgStart();
				msgAdd((uint8)'N');
				msgEnd();
				break;
		}

		last_message = msgbuf[0]; // [AO 2007-10-01] Handle repeating messages
	}

	if (iused <= ieat) // [AO 2007-10-01] Avoid buffer overflow
		iused = 0;
	else
	{
		MoveMemory(ibuf, ibuf + ieat, iused - ieat);
		iused -= ieat;
	}

	return (bool8)(oused != 0);
}

void
RedDogConnection::handleLogin(int32 len)
{
	int32 err = 0;
	int32 balance;

	if (isServerStopping())
	{
		sendStopMsg();
		return;
	}
	
	if (!loggedOn)
	{
		if (!trackHasSavedGame(&err) && err)
		{
			msgStart();
			msgAdd((uint8)'E');
			msgAdd((uint8)(err & 0xff));
			msgEnd();
			return;
		}
		
		if( !trackGetPlayerLimits(&minBet, &maxBet, &compFactor) ||
			!trackGetBalance(&balance) 	)
		{
			Log::msgError("can't get player limits or balance!");
			msgStart();
			msgAdd((uint8)'N');
			msgEnd();
			return;
		}
		
		msgStart();
		msgAdd((uint8)'L');
		msgAdd(balance);
		msgAdd((int32)minBet);
		msgAdd((int32)maxBet);
		msgAdd((uint8)(game ? 1 : 0));
		if (game)
			game->send();
		msgEnd();
		
		loggedOn = true;
	}
	else
	{
		Log::msgWarning("Login: attempt to re-log on  ");
		
		trackaddBadPacket(TRY_LOGON_WHEN_LOGGED_ON,msgbuf[0],(char*)msgbuf,len);
		
		msgStart();
		msgAdd((uint8)'N');
		msgEnd();
	}
}

// [AO 2008-01-16] Refactoring for HA
bool RedDogConnection::ValidateSession()
{
	int32 session_check = trackValidateSession();
	if (!session_check) // session is valid
		return true;

	// [AO 2008-01-22] Additional case from RTG 6.6.0 code drop
	if (session_check == RedDogSQL::NormalLogout)
		session_check = LOGINERR_NOTLOGGEDIN;

	msgStart();
	msgAdd(static_cast<uint8>('N'));
	msgAdd(static_cast<int32>(session_check));
	msgEnd();
	return false;
}
